/*
 * Advanced Methods in Programming 
 * Final Project: Pizza-on-line
 * 
 *   
 *   @author 309170249 317335214
 */
package client;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.HashSet;

import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

import mutual.Order;
import mutual.RMIContracts;
import mutual.absClient;
import mutual.absServer;
import mutual.RMIContracts.Stages;

/* Pizza-on-line GUI client class */

public class pizzaClient extends JFrame implements absClient {
	private class detailsThread implements Runnable {
		private final String id;
		private final String name;
		/**
		 * Thread, wrapping order request to the server
		 * 
		 * @param name
		 * 				pizzaClient's name
		 * @param id
		 * 				old order's id
		 */
		detailsThread(String name, String id) {
			this.id = id;
			this.name = name;
		}

		@Override
		/**
		 * Starts old order query as a thread
		 * 
		 * @param none
		 */
		public void run() {
			try {
				server.getOrder(name, id);
			} catch (RemoteException e) {
				connectionError();
			}
		}
	}

	private static Registry registry = null; //Local registry - server and this client will sign on to it
	private static final long serialVersionUID = -511353411028358356L;
	private String clientName; // Clients are identified by names. Two clients with duplicate names are equal to system!
	private absServer server;  // pizza-online server
	private Order currentOrder; // will hold the state of the last order
	private boolean isOrderRunning; // flag for deciding whether to update the GUI upon notification from server

	private JLabel orderIdLabel;
	private JLabel priceLabel;
	private JLabel stageLabel;
	private JButton getOrderButton;
	private JCheckBox extras[];
	/**
	 * a new order initialization
	 * 
	 * @param none
	 */
	private void initOrder() {
		// use hashString to avoid conflict with other clients
		this.currentOrder = new Order(hashString());
	}
	/**
	 * Updates client GUI according to a new status of clients's order 
	 * 
	 * @param stage
	 * 				 new status of a client's order
	 */
	public void notifyStage(Stages stage) {
		// no updates while no order running and/or old order is present on screen
		if (! this.isOrderRunning)
			return; //for details - consult documentation 
		// the server was not able to update last order - wrong id or already in oven or delivery
		if (stage == Stages.CANTUPDATE) {
			JOptionPane.showMessageDialog(null, "Can't update your order. Already in oven",
					"Sorry", JOptionPane.CANCEL_OPTION);
			return;
		}
		
		this.stageLabel.setText(stage.toString());
		if (stage == Stages.DONE) {
			initOrder();
			orderIdLabel.setText(" ");
			priceLabel.setText(" ");
		}
	}
	/**
	 * See documentation - set after discussing with Yachin
	 * @return String, representing the hash code of client name
	 */
	public String hashString() {
		// not really doing anything. if implemented - would be hard to test :)
		Integer h = this.clientName.hashCode();
		return h.toString();
	}

	/**
	 * hides default ctor
	 */
	private pizzaClient() {	}
	
	/**
	 * Notifies pizzaClient on connection error
	 * 
	 */
	private void connectionError() {
		JOptionPane.showMessageDialog(null, "Server refused connection",
				"Connection error", JOptionPane.ERROR_MESSAGE);
		done(); //end of life for this client
	}
	
	/**
	 * Connects a pizzaClient with a pizzaServer  
	 */
	private void establishConnection() {
		try {
			this.server = (absServer) RMIContracts
					.getServer(RMIContracts.serverName);

		} catch (Exception e) {
			connectionError();
		}

		if (this.server == null)
			connectionError();

	}
	
	/**
	 * pizzaClient's GUI initialization  
	 * 
	 */
	private void setMyLayout() {
		orderIdLabel = new JLabel(" ");
		stageLabel = new JLabel(" ");
		priceLabel = new JLabel(" ");

		JPanel panel = new JPanel();
		JPanel stats = new JPanel();
		JPanel orderData = new JPanel();
		JPanel buttons = new JPanel();

		JButton order = new JButton("Order");
		order.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				order();
			}
		});

		getOrderButton = new JButton("old Order");
		getOrderButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				getOrder();
			}
		});

		extras = new JCheckBox[Order.validExtrasNames.length];
		
		int i = 0;
		// Note: we are not assuming anything about the amount of available
		// toppings...
		for (String extra : Order.validExtrasNames) {
			extras[i] = new JCheckBox(extra);
			orderData.add(extras[i]);
			i++;
		}

		panel.setLayout(new BorderLayout());
		buttons.setLayout(new BorderLayout());

		orderData.setLayout(new BoxLayout(orderData, BoxLayout.PAGE_AXIS));

		buttons.setBorder(BorderFactory.createRaisedBevelBorder());
		stats.setBorder(BorderFactory.createRaisedBevelBorder());

		stats.add(orderIdLabel);
		stats.add(priceLabel);
		orderData.add(stageLabel);
		buttons.add(order, BorderLayout.LINE_START);
		buttons.add(getOrderButton, BorderLayout.LINE_END);
		panel.add(stats, BorderLayout.PAGE_START);
		panel.add(orderData, BorderLayout.CENTER);
		panel.add(buttons, BorderLayout.PAGE_END);
		this.add(panel);
		this.pack();
		this.setVisible(true);
	}
	/**
	 * pizzaClient ctor 
	 * 
	 * @param name 
	 * 				a name of a pizzaClient ( used for a client identification )
	 */
	public pizzaClient(String name) {
		super(name);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.isOrderRunning = false;
		this.clientName = name;
		initOrder();
		establishConnection();
		setMyLayout();

	}
	/**
	 * Notifies pizzaClient on old order's properties ( id, price , etc ) 
	 * 
	 * @param order 
	 * 				old order
	 */
	private void showOrder(Order order) {
	
		stageLabel.setText("");
		priceLabel.setText("<html><b><u>Price:</u></b> "
				+ order.getPrice() + "</html>");
		orderIdLabel.setText("<html><b><u>Order id:</u></b> "
				+ order.getOrderId() + "</html>");
		for ( int i = 0; i < Order.validExtrasNames.length;i++ ){
			if ( order.getExtras().contains(Order.validExtrasNames[i])  )
					extras[i].setSelected(true);
			else
					extras[i].setSelected(false);
		}
	}

	/**
	 * Processing pizzaClient oldOrder query
	 * starting a new thread to take care of the query  
	 * 
	 */
	public void getOrder() {
		String oldId = JOptionPane.showInputDialog(null);
		if (null == oldId || oldId.isEmpty()) {
			JOptionPane.showMessageDialog(null, "Enter correct id", "Id error",
					JOptionPane.ERROR_MESSAGE);
			return;
		}

		if (this.currentOrder.getOrderId().equals(oldId)) {
			JOptionPane.showMessageDialog(null, "This order is not done yet",
					"Id error", JOptionPane.ERROR_MESSAGE);
			return;
		}

		getOrderButton.setEnabled(false);
		detailsThread details = new detailsThread(hashString(), oldId);
		new Thread(details).start();
	}
	/**
	 * Notifies pizzaClient on old order's properties ( id, price , etc ) 
	 * 
	 * @param order
	 * 					old order
	 */
	public void orderDetails(Order order) {
		if (!getOrderButton.isEnabled()) {
			getOrderButton.setEnabled(true);
			if (order == null) {
				JOptionPane.showMessageDialog(null,
						"This order is not in your orders list", "Id error",
						JOptionPane.ERROR_MESSAGE);
				return;
			}
			this.isOrderRunning = false;
			showOrder(order);
			return;
		}
		this.currentOrder = order;
		this.orderIdLabel.setText("<html><b><u>Order id:</u></b> "
				+ order.getOrderId() + "</html>");
	}
	/**
	 * Parses pizzaClient GUI to get toppings for a current order
	 * 
	 */
	private void parseExtrasToOrder() {
		HashSet<String> extrasNames = new HashSet<String>();
		for (JCheckBox box : extras) {
			if (box.isSelected())
				extrasNames.add(box.getText());
		}

		this.currentOrder.setExtras(extrasNames);

	}
	/**
	 * Performs pizzaClient new order's processing
	 * by starting an anonymous thread that  
	 * sends a new order query to PizzaServer 
	 * 
	 * @param none
	 */
	// public for easy unit testing
	public void order() {
		if ( ! this.isOrderRunning ) //if this is not update, but "remake" of old order...
			currentOrder.initId();
		parseExtrasToOrder();
		this.priceLabel.setText("<html><b><u>Price:</u></b> "
				+ this.currentOrder.getPrice() + "</html>");

		new Thread(new Runnable() {
			public void run() {
				try {
					server.order(currentOrder);
				} catch (RemoteException e) {
					connectionError();
				}
			}
		}).start();
		this.isOrderRunning = true;
	}
	/**
	 * Gently kills a pizzaClient
	 * 
	 * @param none
	 */
	private void done() {
		this.dispose();
		System.exit(0);
	}
	/**
	 * main function for a pizzaClient
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		String name = "Dijkstra";
		if (args.length > 0 && !args[0].isEmpty())
			name = args[0];

		if (registry == null)
			try {
				registry = LocateRegistry.getRegistry(
						RMIContracts.serverAddress,
						RMIContracts.serverPort);
			} catch (RemoteException e) {
				System.out.println("No Registry process");
			}
		pizzaClient client = new pizzaClient(name);
		try {// use hashString to avoid conflict with other clients
			RMIContracts.registerServer(registry, client
					.hashString(), UnicastRemoteObject.exportObject(client, 0));
		} catch (RemoteException e) {
			System.out.println("Could not register on registry");
		}

	}

}
